package lysjmaster.base.gateway.intercept;

import cn.hutool.cache.Cache;
import cn.hutool.core.util.ReUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import lysjmaster.base.common.CommonConstant;
import lysjmaster.base.common.authentication.AuthenticationInterface;
import lysjmaster.base.common.authentication.AuthenticationResultVO;
import lysjmaster.base.common.cache.CacheEnum;
import lysjmaster.base.common.cache.LocalCacheUtil;
import lysjmaster.base.common.jwt.JwtUtil;
import lysjmaster.base.common.web.ParamUtil;
import lysjmaster.base.common.web.Resp;
import lysjmaster.base.common.web.RespUtil;
import lysjmaster.base.gateway.config.IgnoredUrls;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author Created by zk on 2019-12-06 15:10
 * @description
 */
@Slf4j
@Component
public class AuthIntercept implements HandlerInterceptor {


    @Resource
    private IgnoredUrls ignoredTokenUrls;
    private List<String> authIgnoredReg;

    @PostConstruct
    public void initIgnoredUrls() {
        authIgnoredReg = new ArrayList<>(ignoredTokenUrls.getUrls().size());
        for (String s : ignoredTokenUrls.getUrls()) {
            authIgnoredReg.add(getRegPath(s.toLowerCase()));
        }
    }

    @Resource
    private LocalCacheUtil localCacheUtil;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        httpServletRequest.setCharacterEncoding("UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html;charset=UTF-8");
        log.info("intercept {} ---------> request_ip ---------> {}", httpServletRequest.getRequestURL(), httpServletRequest.getRemoteAddr());
        String token = httpServletRequest.getHeader(CommonConstant.TOKEN_HEADER_NAME);
        String path = httpServletRequest.getRequestURI();
        if (ignoredToken(path)) {
            return true;
        }
        if (ParamUtil.isBlank(token)) {
            httpServletResponse.getWriter().write(JSONUtil.toJsonStr(RespUtil.error(Resp.ResStatus.NO_LOGIN, "您尚未登录")));
            return false;
        }
        Map<String, Object> payloadMap = JwtUtil.getPayloadMap(token);
        String iss = (String) payloadMap.get(CommonConstant.JWT_ISS);
        if (ParamUtil.isBlank(iss)) {
            httpServletResponse.getWriter().write(JSONUtil.toJsonStr(RespUtil.error(Resp.ResStatus.TOKEN_ERROR, "token有误,请重新登录")));
            return false;
        }
        Cache<String, AuthenticationInterface> authenticationInterfaceCache = localCacheUtil.get(CacheEnum.AUTHENTICATION);
        AuthenticationInterface auth = authenticationInterfaceCache.get(iss);
        if (auth != null) {
            auth = authenticationInterfaceCache.get(iss);
            AuthenticationResultVO res = auth.authentication(token, payloadMap, path);
            if (res.isResult()) {
                httpServletResponse.setHeader(CommonConstant.TOKEN_HEADER_NAME, res.getMessage());
                httpServletRequest.setAttribute(CommonConstant.JWT_PAYLOAD_KEY, payloadMap);
                return true;
            } else {
                httpServletResponse.getWriter().write(JSONUtil.toJsonStr(RespUtil.error(Resp.ResStatus.TOKEN_ERROR, res.getMessage())));
                return false;
            }
        } else {
            httpServletResponse.getWriter().write(JSONUtil.toJsonStr(RespUtil.error(Resp.ResStatus.TOKEN_ERROR, "token有误,请重新登录")));
            return false;
        }
    }

    private boolean ignoredToken(String url) {
        url = url.toLowerCase();
        for (String s : authIgnoredReg) {
            if (ReUtil.isMatch(s, url)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    private String getRegPath(String path) {
        char[] chars = path.toCharArray();
        int len = chars.length;
        StringBuilder sb = new StringBuilder();
        boolean preX = false;
        for (int i = 0; i < len; i++) {
            if (chars[i] == '*') {//遇到*字符
                if (preX) {//如果是第二次遇到*，则将**替换成.*
                    sb.append(".*");
                    preX = false;
                } else if (i + 1 == len) {//如果是遇到单星，且单星是最后一个字符，则直接将*转成[^/]*
                    sb.append("[^/]*");
                } else {//否则单星后面还有字符，则不做任何动作，下一把再做动作
                    preX = true;
                    continue;
                }
            } else {//遇到非*字符
                if (preX) {//如果上一把是*，则先把上一把的*对应的[^/]*添进来
                    sb.append("[^/]*");
                    preX = false;
                }
                if (chars[i] == '?') {//接着判断当前字符是不是?，是的话替换成.
                    sb.append('.');
                } else {//不是?的话，则就是普通字符，直接添进来
                    sb.append(chars[i]);
                }
            }
        }
        return sb.toString();
    }
}
